package com.tools.web.interceptor.auth;

import com.fasterxml.jackson.core.type.TypeReference;
import com.tools.common.container.MapEnum;
import com.tools.common.container.OwnPairs;
import com.tools.common.container.RowKit;
import com.tools.common.container.r.OnlyReadArrayList;
import com.tools.common.container.r.OnlyReadHashMap;
import com.tools.common.object.C;
import com.tools.common.object.json.JsonKit;
import com.tools.common.object.Note;
import com.tools.common.object.ObjKit;
import com.tools.common.object.string.VerifyKit;

import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 用户认证包装类对象的抽象实现
 * */
@Note("用户认证包装类对象的抽象实现")
public abstract class GenericAuthUser implements AuthUser {

    @Note("用户/账户的唯一标识，一般为主键 ID")
    private Object userId;

    @Note("用户实体")
    private Object user;

    @Note("用户对象的字节码对象")
    private Class<?> userTypeClass;

    @Note("角色 key 与角色名称/对象的 Map 集合")
    private Map<String, ?> roleKeyMap;

    @Note("权限 key 与权限名称/对象的 Map 集合")
    private Map<String, ?> permissionKeyMap;

    @Note("用户/账户的前端路由列表的 JSON 字符串，将其缓存到 token 的缓存容器里以减轻查询数据库的压力")
    private List<?> routerList;

    @Note("若还需要存储额外的数据，可以保存至这里")
    private Map<String, Object> customData;

    /* **************************************************************************************
     *
     *          构造器
     *
     * **************************************************************************************
     * */

    protected GenericAuthUser() {}

    protected GenericAuthUser(Object userId, Object user,
                    Map roleKeyMap, Map permissionKeyMap, Iterable<?> routers) {
        this.setUserId(userId);
        this.setRoleKeyMap(roleKeyMap);
        this.setPermissionKeyMap(permissionKeyMap);
        this.setRouterList(routers);
        if(user != null) {
            this.setUserTypeClass(user.getClass().getName());
            this.setUser(user);
        }
    }

    /* **************************************************************************************
     *
     *          Getter/Setter
     *
     * **************************************************************************************
     * */

    @Override
    public final Object getUserId() {
        return userId;
    }

    @Override
    public final  <ID> ID getUserId(Class<ID> idType) {
        return (ID) userId;
    }

    @Override
    public final  <ID> ID getUserId(C<ID> idType) {
        return (ID) ObjKit.toRealValue(userId, idType);
    }

    @Override
    public final void setUserId(Object userId) {
        if(userId == null) throw new NullPointerException("用户 ID 不能为 null");
        if((userId instanceof String) && ((String) userId).isEmpty()) {
            throw new IllegalArgumentException("用户 ID 不能为空");
        }
        this.userId = userId;
    }

    @Override
    public final Object getUser() {
        return this.user;
    }

    @Override
    public final <U> U getUser(Class<U> userClass) {
        return (U) this.user;
    }

    @Override
    public final <U> U getUser(C<U> userType) {
        return (U) ObjKit.toRealValue(this.user, userType);
    }

    @Override
    public final void setUser(Object user) {
        this.user = user;
    }

    @Override
    public Class<?> getUserTypeClass() {
        return userTypeClass;
    }

    @Override
    public void setUserTypeClass(String userTypeClassName) {
        this.userTypeClass = ObjKit.getObjectClass(userTypeClassName);
    }

    @Override
    public final Map<String, ?> getRoleKeyMap() {
        return roleKeyMap;
    }

    @Override
    public final <V> Map<String, V> getRoleKeyMap(Class<V> valueType) {
        return (Map<String, V>) roleKeyMap;
    }

    @Override
    public final Set<String> roleKeySet() {
        if(roleKeyMap == null || roleKeyMap.isEmpty()) return new HashSet<>();
        return new HashSet<>(roleKeyMap.keySet());
    }

    @Override
    public final Collection<?> roleValueCollection() {
        if(roleKeyMap == null || roleKeyMap.isEmpty()) return new ArrayList<>();
        return new ArrayList<>(roleKeyMap.values());
    }

    @Override
    public final <V> Collection<V> roleValueCollection(Class<V> valueType) {
        return (Collection<V>) this.roleValueCollection();
    }

    @Override
    public final <V> Collection<V> roleValueCollection(C<V> valueType) {
        return RowKit.toArrayList(this.roleValueCollection(), valueType);
    }

    @Override
    public Map<String, ?> cloneRoleKeyMap() {
        return (this.permissionKeyMap == null || this.permissionKeyMap.isEmpty())
                ? new HashMap<>()
                : new HashMap<>(this.permissionKeyMap);
    }

    @Override
    public <V> Map<String, V> cloneRoleKeyMap(Class<V> valueType) {
        return (Map<String , V>) cloneRoleKeyMap();
    }

    @Override
    public <V> Map<String, V> cloneRoleKeyMap(C<V> valueType) {
        if(roleKeyMap == null || roleKeyMap.isEmpty()) return new HashMap<>();
        Map<String, V> result = new HashMap<>((int) (roleKeyMap.size() / 0.75) + 1);
        for(Map.Entry<String, ?> e : roleKeyMap.entrySet()) {
            result.put(e.getKey(), (V) ObjKit.toRealValue(e.getValue(), valueType));
        }
        return result;
    }

    @Override
    public final void setRoleKeyMap(Map roleKeyMap) {
        Map<String, Object> temp = this.private_getTempMap(roleKeyMap);
        this.roleKeyMap = new OnlyReadHashMap<>(temp);
    }

    @Override
    public final Map<String , ?> getPermissionKeyMap() {
        return permissionKeyMap;
    }

    @Override
    public final  <V> Map<String, V> getPermissionKeyMap(Class<V> valueType) {
        return (Map<String , V>) permissionKeyMap;
    }

    @Override
    public final Set<String> permKeySet() {
        if(permissionKeyMap == null || permissionKeyMap.isEmpty()) return new HashSet<>();
        return new HashSet<>(permissionKeyMap.keySet());
    }

    @Override
    public final Collection<?> permValueCollection() {
        if(permissionKeyMap == null || permissionKeyMap.isEmpty()) return new ArrayList<>();
        return new ArrayList<>(permissionKeyMap.values());
    }

    @Override
    public final <V> Collection<V> permValueCollection(Class<V> valueType) {
        return (Collection<V>) this.permValueCollection();
    }

    @Override
    public final <V> Collection<V> permValueCollection(C<V> valueType) {
        return RowKit.toArrayList(this.permValueCollection(), valueType);
    }

    @Override
    public Map<String, ?> clonePermissionKeyMap() {
        return (this.permissionKeyMap == null || this.permissionKeyMap.isEmpty())
                ? new HashMap<>()
                : new HashMap<>(this.permissionKeyMap);
    }

    @Override
    public <V> Map<String, V> clonePermissionKeyMap(Class<V> valueType) {
        return (Map<String , V>) clonePermissionKeyMap();
    }

    @Override
    public <V> Map<String, V> clonePermissionKeyMap(C<V> valueType) {
        if(permissionKeyMap == null || permissionKeyMap.isEmpty()) return new HashMap<>();
        Map<String, V> result = new HashMap<>((int) (permissionKeyMap.size() / 0.75) + 1);
        Set<? extends Map.Entry<String, ?>> entries = permissionKeyMap.entrySet();
        for(Map.Entry<String, ?> e : entries) {
            result.put(e.getKey(), (V) ObjKit.toRealValue(e.getValue(), valueType));
        }
        return result;
    }

    @Override
    public void setPermissionKeyMap(Map permKeyMap) {
        Map<String, Object> temp = this.private_getTempMap(permKeyMap);
        this.permissionKeyMap = new OnlyReadHashMap<>(temp);
    }

    @Override
    public List<?> getRouterList() {
        return routerList;
    }

    @Override
    public <V> List<V> getRouterList(Class<V> routerType) {
        return (List<V>) routerList;
    }

    @Override
    public List<?> cloneRouterList() {
        return (this.routerList == null || this.routerList.isEmpty())
                ? new ArrayList<>()
                : new ArrayList<>(this.routerList);
    }

    @Override
    public <V> List<V> cloneRouterList(Class<V> routerType) {
        return (List<V>) cloneRouterList();
    }

    @Override
    public <V> List<V> cloneRouterList(C<V> routerType) {
        return (this.routerList == null || this.routerList.isEmpty())
                ? new ArrayList<>()
                : RowKit.toArrayList(routerType, routerList);
    }

    @Override
    public final void setRouterList(Iterable routerList) {
        this.routerList = new OnlyReadArrayList<>(routerList);
    }

    @Override
    public Map<String, Object> cloneCustomData() {
        return (customData == null || customData.isEmpty())
                ? new HashMap<>()
                : new HashMap<>(customData);
    }

    protected final Map<String, Object> getCustomData() {
        return customData;
    }

    protected final void setCustomData(Map<String, Object> customData) {
        this.customData = customData;
    }

    @Override
    public String toString() {
        return "userId=" + userId +
                this.protected_toStringPropertyString() +
                ", user=" + user +
                ", userTypeClass=" + userTypeClass +
                ", roleKeyMap=" + roleKeyMap +
                ", permissionKeyMap=" + permissionKeyMap +
                ", routerList=" + routerList +
                ", customData=" + customData;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        GenericAuthUser that = (GenericAuthUser) o;
        return Objects.equals(userId, that.userId) && Objects.equals(user, that.user) && Objects.equals(userTypeClass, that.userTypeClass)  && Objects.equals(roleKeyMap, that.roleKeyMap) && Objects.equals(permissionKeyMap, that.permissionKeyMap) && Objects.equals(routerList, that.routerList) && Objects.equals(customData, that.customData);
    }

    @Override
    public int hashCode() {
        return Objects.hash(userId, user, userTypeClass, roleKeyMap, permissionKeyMap, routerList, customData);
    }


    /* **************************************************************************************
     *
     *          子类可用/必须实现的逻辑
     *
     * **************************************************************************************
     * */

    @Note("子类 toString 的部分")
    protected abstract String protected_toStringPropertyString();

    @Note("拷贝用户的基本数据、角色、权限、路由等数据到新的认证对象里")
    protected final void copyBaseUserDataTo(GenericAuthUser targetAuthUser) {
        targetAuthUser.userId = this.userId;
        targetAuthUser.user = this.user;
        targetAuthUser.userTypeClass = this.userTypeClass;
        targetAuthUser.roleKeyMap = (this.roleKeyMap == null || this.roleKeyMap.isEmpty())
                ? null
                : new OnlyReadHashMap<>(this.roleKeyMap);
        targetAuthUser.permissionKeyMap = (this.permissionKeyMap == null || this.permissionKeyMap.isEmpty())
                ? null
                : new OnlyReadHashMap<>(this.permissionKeyMap);
        targetAuthUser.routerList = (this.routerList == null || this.routerList.isEmpty())
                ? null
                : new OnlyReadArrayList<>(this.routerList);
    }


    @Note("拷贝自定义数据到新的 GenericAuthUser 对象中")
    protected final void copyCustomDataMapTo(GenericAuthUser newAuthUser) {
        newAuthUser.customData = (this.customData == null || this.customData.isEmpty())
                ? null
                : new ConcurrentHashMap<>(this.customData);
    }

    /* **************************************************************************************
     *
     *          自定义数据容器的功能方法
     *
     * **************************************************************************************
     * */

    @Override
    public GenericAuthUser userId(Object userId) {
        this.setUserId(userId);
        return this;
    }

    @Override
    public GenericAuthUser user(Object user) {
        this.setUser(user);
        return this;
    }

    @Override
    public GenericAuthUser userTypeClass(String userTypeClassName) {
        this.setUserTypeClass(userTypeClassName);
        return this;
    }

    @Override
    public GenericAuthUser roleKeyMap(Map roleKeyMap) {
        this.setRoleKeyMap(roleKeyMap);
        return this;
    }

    @Override
    public GenericAuthUser permissionKeyMap(Map permissionKeyMap) {
        this.setPermissionKeyMap(permissionKeyMap);
        return this;
    }

    @Override
    public GenericAuthUser routerList(Iterable routerList) {
        this.setRouterList(routerList);
        return this;
    }

    @Override
    public GenericAuthUser putData(String name, Object value) {
        private_lazyLoadCustomDataMap(1);
        this.customData.put(name, value);
        return this;
    }

    @Override
    public GenericAuthUser putAllData(Map customData) {
        if(customData == null || customData.isEmpty()) return this;
        private_lazyLoadCustomDataMap(customData.size());
        private_putCustomDataEntry(customData.entrySet());
        return this;
    }

    @Override
    public GenericAuthUser putAllData(OwnPairs customData) {
        if(customData == null || customData.isEmpty()) return this;
        private_lazyLoadCustomDataMap(customData.size());
        private_putCustomDataEntry(customData.entrySet());
        return this;
    }

    @Override
    public GenericAuthUser putAllData(String jsonObject) {
        if(VerifyKit.isJsonObject(jsonObject)) {
            HashMap<String, Object> map = JsonKit.toEntity(jsonObject, new TypeReference<HashMap<String, Object>>() {});
            if(map.isEmpty()) return this;
            private_lazyLoadCustomDataMap(map.size());
            private_putCustomDataEntry(map.entrySet());
        }
        return this;
    }

    @Override
    public GenericAuthUser putAllData(Object javaBean) {
        if(javaBean == null) return this;
        Map<String, Object> fieldValueMap = ObjKit.allFieldValue(javaBean);
        if(fieldValueMap.isEmpty()) return this;
        private_lazyLoadCustomDataMap(fieldValueMap.size());
        private_putCustomDataEntry(fieldValueMap.entrySet());
        return this;
    }


    @Override
    public GenericAuthUser removeData(String name) {
        if(this.customData != null && !this.customData.isEmpty()) this.customData.remove(name);
        return this;
    }

    @Override
    public GenericAuthUser removeAllData(String... names) {
        if(this.customData != null && !this.customData.isEmpty()) {
            for (String name : names) this.customData.remove(name);
        }
        return this;
    }

    @Override
    public GenericAuthUser removeAllData(Iterable<String> names) {
        if(this.customData != null && !this.customData.isEmpty()) {
            for (String name : names) this.customData.remove(name);
        }
        return this;
    }

    @Override
    public GenericAuthUser clearData() {
        if(this.customData != null && !this.customData.isEmpty()) this.customData.clear();
        return this;
    }

    @Override
    public boolean containsData(String name) {
        if(this.customData != null && !this.customData.isEmpty()) return this.customData.containsKey(name);
        return false;
    }

    @Override
    public boolean containsAllData(String... names) {
        if(this.customData != null && !this.customData.isEmpty()) {
            for (String n : names) {
                if (!this.customData.containsKey(n)) return false;
            }
            return true;
        }
        return false;
    }

    @Override
    public boolean containsAllData(Iterable<String> names) {
        if(this.customData != null && !this.customData.isEmpty()) {
            for (String n : names) {
                if (!this.customData.containsKey(n)) return false;
            }
            return true;
        }
        return false;
    }

    @Override
    public boolean containsAnyData(String... names) {
        if(this.customData != null && !this.customData.isEmpty()) {
            for (String n : names) {
                if (this.customData.containsKey(n)) return true;
            }
            return false;
        }
        return false;
    }

    @Override
    public boolean containsAnyData(Iterable<String> names) {
        if(this.customData != null && !this.customData.isEmpty()) {
            for (String n : names) {
                if (this.customData.containsKey(n)) return true;
            }
            return false;
        }
        return false;
    }

    @Override
    public int dataSize() {
        return (this.customData == null || this.customData.isEmpty()) ? 0 : this.customData.size();
    }

    @Override
    public Object dataGet(String name) {
        if(this.customData == null || this.customData.isEmpty()) return null;
        return this.customData.get(name);
    }

    @Override
    public <V> V dataGet(String name, Class<V> valueType) {
        return (V) this.dataGet(name);
    }

    @Override
    public <V> V dataGet(String name, C<V> valueType) {
        if(this.customData == null || this.customData.isEmpty()) return null;
        return (V) ObjKit.toRealValue(this.dataGet(name), valueType);
    }

    /* **************************************************************************************
     *
     *          私有逻辑
     *
     * **************************************************************************************
     * */


    @Note("将自定义的数据放入集合内")
    private void private_putCustomDataEntry(Set entrySet) {
        for (Object o : entrySet) {
            Map.Entry e = (Map.Entry) o;
            this.customData.put(ObjKit.asStringValue(e.getValue(), null),
                    e.getValue());
        }
    }

    @Note("懒加载自定义数据 Map 集合")
    private void private_lazyLoadCustomDataMap(int initSize) {
        if(this.customData == null) {
            this.customData = new HashMap<>(MapEnum.calcInitSize(initSize));
        }
    }


    @Note("将 src 的数据拷贝到 temp 里返回")
    private Map<String, Object> private_getTempMap(Map src) {
        if(src == null || src.isEmpty()) return null;
        Map<String, Object> temp = new HashMap<>((int) (src.size() / 0.75) + 1);
        for(Object o : src.entrySet()) {
            Map.Entry e = (Map.Entry) o;
            temp.put(e.getKey().toString(), e.getValue());
        }
        return temp;
    }
}
